summaryrefslogtreecommitdiffstats
path: root/src/Entities/Entity.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Entities/Entity.cpp')
-rw-r--r--src/Entities/Entity.cpp58
1 files changed, 40 insertions, 18 deletions
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 23a29a3a4..e0ea32e78 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -73,6 +73,7 @@ cEntity::cEntity(eEntityType a_EntityType, Vector3d a_Pos, double a_Width, doubl
m_Height(a_Height),
m_InvulnerableTicks(0)
{
+ m_WorldChangeInfo.m_NewWorld = nullptr;
}
@@ -1406,7 +1407,7 @@ bool cEntity::DetectPortal()
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
LOGD("Jumping %s -> %s", DimensionToString(dimNether).c_str(), DimensionToString(DestionationDim).c_str());
- new cNetherPortalScanner(this, TargetWorld, TargetPos, cChunkDef::Height);
+ new cNetherPortalScanner(*this, TargetWorld, TargetPos, cChunkDef::Height);
return true;
}
// Nether portal in the overworld
@@ -1438,7 +1439,7 @@ bool cEntity::DetectPortal()
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName());
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
LOGD("Jumping %s -> %s", DimensionToString(dimOverworld).c_str(), DimensionToString(DestionationDim).c_str());
- new cNetherPortalScanner(this, TargetWorld, TargetPos, (cChunkDef::Height / 2));
+ new cNetherPortalScanner(*this, TargetWorld, TargetPos, (cChunkDef::Height / 2));
return true;
}
}
@@ -1587,25 +1588,46 @@ bool cEntity::MoveToWorld(cWorld * a_World, Vector3d a_NewPosition, bool a_SetPo
return false;
}
+ if (m_WorldChangeInfo.m_NewWorld != nullptr)
+ {
+ // Avoid scheduling multiple warp tasks
+ return true;
+ }
+
// Create new world change info
- auto NewWCI = cpp14::make_unique<sWorldChangeInfo>();
- *NewWCI = { a_World, a_NewPosition, a_SetPortalCooldown, a_ShouldSendRespawn };
+ m_WorldChangeInfo = { a_World, a_NewPosition, a_SetPortalCooldown, a_ShouldSendRespawn };
- // Publish atomically
- auto OldWCI = m_WorldChangeInfo.exchange(std::move(NewWCI));
+ // TODO: move to capture when C++14
+ const auto EntityID = GetUniqueID();
- if (OldWCI == nullptr)
- {
- // Schedule a new world change.
- GetWorld()->QueueTask(
- [this](cWorld & a_CurWorld)
- {
- auto WCI = m_WorldChangeInfo.exchange(nullptr);
- cWorld::cLock Lock(a_CurWorld);
- DoMoveToWorld(*WCI);
- }
- );
- }
+ /* Requirements:
+ Only one world change in-flight at any time
+ No ticking during world changes
+ The last invocation takes effect
+
+ As of writing, cWorld ticks entities, clients, and then processes tasks
+ We may call MoveToWorld (any number of times - consider multiple /portal commands within a tick) in the first and second stages
+ Queue a task onto the third stage to invoke DoMoveToWorld ONCE with the last given destination world
+ Store entity IDs in case client tick found the player disconnected and immediately destroys the object
+
+ After the move begins, no further calls to MoveToWorld is possible since neither the client nor entity is ticked
+ This remains until the warp is complete and the destination world resumes ticking.
+ */
+ GetWorld()->QueueTask(
+ [EntityID](cWorld & a_CurWorld)
+ {
+ a_CurWorld.DoWithEntityByID(
+ EntityID,
+ [](cEntity & a_Entity)
+ {
+ auto & WCI = a_Entity.m_WorldChangeInfo;
+ a_Entity.DoMoveToWorld(WCI);
+ WCI.m_NewWorld = nullptr;
+ return true;
+ }
+ );
+ }
+ );
return true;
}